package Image::BoxModel::Text;

use warnings;
use strict;

use POSIX;

=head1 NAME

Image::BoxModel::Text - Text function for Image::BoxModel

=head1 SYNOPSIS

  For an example and general information see Image::BoxModel.pm

=head1 DESCRIPTION

Image::BoxModel::Text implements direct inserting of text. It has the following method 'Annotate' which a Image::BoxModel object inherits.

It uses Image::BoxModel::Lowlevel for defining boxes and drawing text. See there for more information. 

'Annotate' will guarantee that every text lives in its own box and avoids many possible bugs, like text being overwritten by other texts in the same box, to small boxes..

Anyway, if you have a good reason, feel free to use the methods from ::Lowlevel directly. This can end in completely non-box-model-style images. 

=head2 Method

=head3 Annotate

 $name_of_box = $image -> Annotate (
	text 			=> $text 					# mandatory
	name 			=> $name_of_box
	position 		=> [top|bottom|right|left],
	textsize 		=> $size,
	font 			=> $font,					
	rotate 			=> [in degrees, may be negative as well],
	align	 		=> [Center|Left|Right],		# align is how multiline-text is aligned
	text_position 	=> [Center					# position is how text will be positioned inside its box
						NorthWest|
						North|
						NorthEast|
						West|
						SoutEast|
						South|
						SouthWest|
						West],
	background 		=> (color),
	padding_right 	=> [number],
	padding_left 	=> [number],
	padding_top 	=> [number],
	padding_bottom	=> [number],
 )

All parameters except "text" are preset with defaults. These are the first value above or generally "0" for numbers (except "12" for textsize), and "white" for colors.

$name_of_box is a number, starting from 1.

=cut

sub Annotate{
	my $image = shift;
	my %p = (
		position=>"top",
		textsize => 12,
		rotate => 0,
		align => "Center",
		padding_right => 0,
		padding_left => 0,
		padding_top => 0,
		padding_bottom => 0,
		color => 'black',
		@_
	);
	
	die __PACKAGE__, " Mandatory parameter 'text' missing. Die" unless (exists $p{text} and $p{text});
	
	my $resize = $p{resize} || 'free';
	
	my $box_position = "top";
	my $text_position = "Center";
	
	$box_position = $p{box_position} if (exists $p{box_position});
	$text_position = $p{text_position} if (exists $p{text_position});
	
	#autogenerated boxes are numbered, starting with 1
	my $e = 1;
	if (exists $p{name}){	# Anyway, if you give it a name, then you want it to call it that way ;-)
		$e = $p{name};
	}
	else{	# autogenerate name: find a previously unused number
		$e++ while (exists $image -> {$e});
	}
	
	my ($width, $height) = $image -> GetTextSize(
		text => $p{text}, 
		textsize => $p{textsize}, 
		rotate => $p{rotate},
		font => $p{font}
	);
	
	$image -> Box(
		resize => $resize,
		position =>$box_position, 
		width=> $width+$p{padding_right}+$p{padding_left}, 
		height => $height+$p{padding_top}+$p{padding_bottom}, 
		name=> $e, 
		background => $p{background}
	);
	
	#if there is some padding, little empty boxes are added:
	foreach ("padding_top", "padding_bottom", "padding_left", "padding_right"){
		(my $position = $_) =~ s/.+_//;
		
		$image-> Box(
			resize=> $e, 
			position =>$position, 
			width=> $p{$_}-1, 
			height => $p{$_}-1, 
			name => $e.$_, 
			background => $p{background} 
		) if ($p{$_} > 0);
	}
	
	$image -> Text(
		box => $e, 
		text=> $p{text}, 
		font => $p{font}, 
		textsize => $p{textsize}, 
		align=>$p{align}, 
		position=> $text_position, 
		rotate => $p{rotate},
		color => $p{color}
	);
	
	return $e;
}

=head3 ArrayBox

 $image -> ArrayBox (
	values => \@array,	#holds the texts
	textsize => [number],
	rotate => [degree],
	resize => [name of box],
	position => [top | bottom | right | left],
	name => [name of new box],
	background => [color]	#optional
 );

Creates a exactly fitting box for an simple array of values. It does not understand what to do with arrays of arrays or the like.
The new box is directly stored into the $image object.

It returns nothing.

Can only be used from Image::BoxModel::Chart objects at the moment. Or manually add Image::BoxModel::Chart::Data to your @ISA and use() it.

If the new box is positioned top or bottom, ArrayBox assumes that the user wants to draw the values side by side, if the position is left or right, that the values are "piled" bottom up or top down. 

=cut

sub ArrayBox{
	my $image = shift;
	my %p = (
		resize => 'free',
		@_
	);
	
	$p{skip} = 1 unless ($p{skip}); #assure that $p{skip} cannot become 0;
	
	my $width = 0; 
	my $height = 0;
	
	my $orientation;
	
	unless (exists $p{name} and $p{name}){
		#autogenerated boxes are numbered, starting with 1
		$p{name} = 1;
		$p{name}++ while (exists $image -> {$p{name}});
	}
	
	#~ print "Name in ArrayBox: $p{name}\n";
	
	if (exists $p{orientation} and ($p{orientation} eq 'vertical' or $p{orientation} eq 'horizontal')){
		$orientation = $p{orientation};
	}
	elsif ($p{position} =~ /right/i or $p{position} =~ /left/i){#guessed that this is desired unless otherwise specified..
		$orientation = 'vertical';
	}
	elsif ($p{position} =~ /top/i or $p{position} =~ /bottom/i){
		$orientation = 'horizontal';
	}
	else{
		die __PACKAGE__, " :parameter $p{orientation} invalid. Valid are 'vertical' and 'horizontal'\n";
	}
	
	#~ print "$p{name}: Orientation: $orientation\n";
	
	my $c = -1;
	
	foreach (@{$p{values_ref}}){
		$c++;
		next unless ($c % $p{skip} == 0);
		
		my ($w, $h) = $image -> GetTextSize(text => $_, font => $p{font}, textsize => $p{textsize}, rotate => $p{rotate});
		if ($orientation eq 'vertical'){	#the values are then printed one upon each other
			$width = $w if ($w > $width);	#..then the longest string determines the size of the box
			$height += $h;					#height is the sum of the heights of each value
			$height ++;						#even I shouldn't guess after I've been writing this module by myself, I guess Box needs 1 "pixel" more than the texts size
		}
		elsif ($orientation eq 'horizontal'){#the values are printed side to side
			$width += $w;					#width is the sum..
			$height = $h if ($h > $height);	#height is determined by the highest string (see above)
			$width ++;						#see my guess above.
		}
	}
	
	# Box only needs width if positioned left or right. - $width is possibly too wide, which is no problem. So we set it = 1 usw.
	$width = 1 if ($orientation eq 'horizontal');
	#~ $height= 1 if ($orientation eq 'vertical');
		
	unless (exists $p{no_box} and $p{no_box}){
		$image -> Box(
			resize => $p{resize}, 
			position =>$p{position}, 
			width=> (ceil ($width)), 
			height => (ceil ($height)), 	#the good thing is, Box only uses the value it needs. if it makes a new box on the left, height is ignored.
			name=> "$p{name}", 
			background => $p{background},
		);
	}
	
	#~ print "In Text::ArrayBox: width: $width, height: $height\n";
	return $width, $height;
}

1
